from sqlalchemy import desc
from typing import Dict, List, TypeVar, Optional
from DB import db

T = TypeVar("T")


class Paginate:
    """
    Paginate object get data
    res = Paginate(result.items, result.page, result.per_page, result.total)
    res.data
    """

    def __init__(self, items, page, size, count):
        self.items = items
        self.page = page
        self.size = size
        self.count = count

    @property
    def data(self):
        return {
            "result": self.items,
            "page": self.page,
            "size": self.size,
            "count": self.count
        }


def paginate(model_class: db.Model,
             p: Dict[str, str],
             filters: Optional[List] = None,
             filter_by: Optional[Dict] = None
             ):
    """
    :param filter_by:
    :param model_class: model_class
    :param p: request params
    :param filters: list
    :return:
    """

    if filter_by is None:
        filter_by = {}
    if filters is None:
        filters = []

    page = int(p.get("page", 1))
    order = p.get("order", 'id')
    per_page = int(p.get("per_page", 10))

    if order.startswith('-'):
        order = order.split("-")[1]
        order = desc(getattr(model_class, order, None))
    sql = db.select(model_class).filter(*filters).order_by(order).filter_by(**filter_by)

    result = db.paginate(sql, page=page, per_page=per_page)
    return Paginate(result.items, result.page, result.per_page, result.total)


def create(model_class: db.Model, data: dict):
    """
    create a model instance
    :param model_class:
    :param data:
    :return:
    """
    model = model_class(**data)
    db.session.add(model)
    db.session.commit()


def get(model_class: T, key_id: int) -> T:
    """
    get instance by primary key
    """
    instance = db.session.get(model_class, key_id)
    return instance


def get_by_ids(model_class: T, ids: List[int]) -> List[T]:
    """
    get instances by list primary key
    return List[instance]
    """
    sql = db.select(model_class).filter(model_class.id.in_(ids))
    instances = db.session.execute(sql).scalars().all()
    return instances


def delete(model_class: db.Model, key_id: int):
    """
    delete instance by primary key
    """
    instance = get(model_class, key_id)
    db.session.delete(instance)


def update(instance: db.Model, data: dict):
    """
    update a model instance
    :param instance:
    :param data:
    :return:
    """
    for key, value in data.items():
        setattr(instance, key, value)
    db.session.commit()


def update_by_id(model_class: db.Model, data: dict, key_id):
    """
    update a instance by  primary key
    """
    instance = get(model_class, key_id)
    if instance:
        update(instance, data)
